﻿using Core.AbstractEntities;
using System;
using Exceptions.Custom;

namespace Operations.Entities
{
    /// <summary>
    /// Specify the operation  for the item added
    /// </summary>
    public class Dispatcher
    {
        /// <summary>
        /// Execute operation for each item added in the stack
        /// </summary>
        private Operation Operation { get; set; }
        /// <summary>
        /// Analyse the element added in the stack
        /// </summary>
        private AnalysisLexical AnalysisLexical { get; set; }
        /// <summary>
        /// Return the element with the operation applied
        /// </summary>
        /// <param name="hpCollection">Collection of items</param>
        public HpElement GetValue(HpCollection hpCollection)
        {
            this.AnalysisLexical = new AnalysisLexical();
            HpElement hpElement = hpCollection.Get();
            HpElement resultOperation = null;

            Operation = FactoryOperation(hpCollection, hpElement);
            resultOperation = Operation.Execute();

            if (AnalysisLexical.IsOperation(hpElement))
                //If is an "operation" we must Pop the 3 last items
                hpCollection.Pop(3);
            else
                // In any other case we Pop only "once"
                hpCollection.Pop();
            //Send the resul of the operation
            return resultOperation;
        }
        /// <summary>
        /// Factory to specify the tye of the operation for the element added
        /// </summary>
        /// <param name="hpCollection">Collection of items</param>
        /// <param name="hpElement">Last element added</param>
        private Operation FactoryOperation(HpCollection hpCollection, HpElement hpElement)
        {
            if (this.AnalysisLexical.IsPrimitive(hpElement))
                return new OperationPrimitive(hpCollection);

            if (this.AnalysisLexical.IsList(hpElement))
                return new OperationPrimitive(hpCollection);

            if (this.AnalysisLexical.IsRPL(hpElement))
                return new OperationRPL(hpCollection);

            if (this.AnalysisLexical.IsCommandUnitary(hpElement))
                return new OperationUnitary(hpCollection);

            if (this.AnalysisLexical.IsCommandBinary(hpElement))
            {
                if (hpCollection.Count <= 1)
                    throw new HpException(string.Format("{0} Error: Too Few Arguments", hpElement.Value));

                // List Number Operator
                if (this.AnalysisLexical.IsList(hpCollection[hpCollection.Count - 2])
                && this.AnalysisLexical.IsNumeric(hpCollection[hpCollection.Count - 3]))
                    return new OperationList(hpCollection);

                // Number Number Operator
                if (this.AnalysisLexical.IsNumeric(hpCollection[hpCollection.Count - 2])
                && this.AnalysisLexical.IsNumeric(hpCollection[hpCollection.Count - 3]))
                    return new OperationBinary(hpCollection);

                // List List Operator
                if (this.AnalysisLexical.IsList(hpCollection[hpCollection.Count - 2])
                    || this.AnalysisLexical.IsList(hpCollection[hpCollection.Count - 3]))
                    return new OperationList(hpCollection);
            }

            return null;
        }

    }
}
